
/**
 * 包装类
 * <p>
 * 为了解决8种基本数据类型的变量不能当成Object类型变量使用的问题，Java提供了包装类（Wrapper Class）的概念，
 * 为8种基本数据类型分别定义了相应的引用类型，并称之为基本数据类型的包装类。
 * <pre>
 *   基本数据类型       包装类
 *      byte            Byte
 *      short           Short
 *      int             Integer
 *      long            Long
 *      char            Character
 *      float           Float
 *      double          Double
 *      boolean         Boolean
 * </pre>
 * 除int和char有点例外之外，其他的基本数据类型对应的包装类都是将其首字母大写即可。
 * <p>
 * 在JDK 1.5以前，把基本数据类型变量变成包装类实例需要通过对应包装类的valueOf()静态方法来实现。
 * 在JDK 1.5以前，如果希望获得包装类对象中包装的基本类型变量，则可以使用包装类提供的xxxValue()实例方法。
 * 但这种用法已经过时。
 * <p>
 * JDK 1.5提供了自动装箱（Autoboxing）和自动拆箱（AutoUnboxing）功能。
 * 所谓自动装箱，就是可以把一个基本类型变量直接赋给对应的包装类变量，或者赋给Object变量（Object是所有类的父类，子类对象可以直接赋给父类变量）；
 * 自动拆箱则与之相反，允许直接把包装类对象直接赋给一个对应的基本类型变量。
 */
public class WrapperClass {
    public static void autoBoxingUnboxing() {
        // 直接把一个基本类型变量赋给Integer对象
        Integer inObj = 5;
        // 直接把一个boolean类型变量赋给一个Object类型的变量
        Object boolObj = true;
        // 直接把一个Integer对象赋给int类型的变量
        int it = inObj;
        if (boolObj instanceof Boolean) {
            // 先把Object对象强制类型转换为Boolean类型，再赋给boolean变量
            boolean b = (Boolean) boolObj;
            System.out.println(b);
        }
        /*
         * 借助于包装类的帮助，再加上JDK 1.5提供的自动装箱、自动拆箱功能，
         * 开发者可以把基本类型的变量“近似”地当成对象使用（所有装箱、拆箱过程都由系统自动完成，无须程序员理会）；
         * 反过来，开发者也可以把包装类的实例近似地当成基本类型的变量使用。
         */
    }

    /**
     * 除此之外，包装类还可实现基本类型变量和字符串之间的转换。把字符串类型的值转换为基本类型的值有两种方式。
     * ➢ 利用包装类提供的parseXxx(String s)静态方法（除Character之外的所有包装类都提供了该方法）。
     * ➢ 利用包装类提供的valueOf(String s)静态方法。
     * <p>
     * String类也提供了多个重载valueOf()方法，用于将基本类型变量转换成字符串，下面程序示范了这种转换关系。
     */
    public static void primitive2String() {
        // 把一个特定字符串转换成int变量
        String intStr = "123";
        int it1 = Integer.parseInt(intStr);
        int it2 = Integer.valueOf(intStr);
        System.out.println(it2);

        // 把一个特定字符串转换成float变量
        String floatStr = "4.56";
        float ft1 = Float.parseFloat(floatStr);
        float ft2 = Float.valueOf(floatStr);
        System.out.println(ft2);

        // 把一个float变量转为String变量
        String ftStr = String.valueOf(2.345f);
        System.out.println(ftStr);

        // 把一个double变量转换为String变量
        String dbStr = String.valueOf(3.344);
        System.out.println(dbStr);

        // 把一个boolean变量转换成String变量
        String boolStr = String.valueOf(true);
        System.out.println(boolStr);

        // 果希望把基本类型变量转换成字符串，还有一种更简单的方法：将基本类型变量和""进行连接运算，系统会自动把基本类型变量转换成字符串。
        String itStr = 5 + "";
        System.out.println(itStr);
    }

    // 包装类的比较
    public static void wrapperClassCompare() {
        // 此处要指出的是，虽然包装类型的变量是引用数据类型，但包装类的实例可以与数值类型的值进行比较，
        // 这种比较是直接取出包装类实例所包装的数值来进行比较的。
        Integer a = 6;
        System.out.println("6的包装类实例是否大于5.0：" + (a > 5.0));

        // 两个包装类的实例进行比较的情况就比较复杂，因为包装类的实例实际上是引用类型，只有两个包装类引用指向同一个对象时才会返回true。
        System.out.println("比较2个包装类的实例是否相等：" + (Integer.valueOf(2) == Integer.valueOf(2)));

        // 但JDK 1.5以后支持所谓的自动装箱，自动装箱就是可以直接把一个基本类型值赋给一个包装类实例，在这种情况下可能会出现一些特别的情形。
        Integer ina = 2;
        Integer inb = 2;
        System.out.println("两个2自动装箱后是否相等：" + (ina == inb));
        Integer biga = 128;
        Integer bigb = 128;
        System.out.println("两个128自动装箱后是否相等：" + (biga == bigb));

        // -128～127之间的同一个整数自动装箱成Integer实例时，永远都是引用cache数组的同一个数组元素，所以它们全部相等；
        // Java7增强了包装类的功能，所有的包装类都提供了一个静态的compare（xxx val1，xxx val2）方法
        // 用来比较两个基本类型值的大小，包括比较两个boolean类型值，两个boolean类型值进行比较时，true>false。
        System.out.println(Boolean.compare(true, false));
        System.out.println(Boolean.compare(true, true));
        System.out.println(Boolean.compare(false, false));
    }

    /**
     * Java 8再次增强了这些包装类的功能，其中一个重要的增强就是支持无符号算术运算。Java 8为整型包装类增加了支持无符号运算的方法。
     * Java 8为Integer、Long增加了如下方法。
     * ➢ static String toUnsignedString(int/long i)：该方法将指定int或long型整数转换为无符号整数对应的字符串。
     * ➢ static String toUnsignedString(int i/long,int radix)：该方法将指定int或long型整数转换为指定进制的无符号整数对应的字符串。
     * ➢ static xxx parseUnsignedXxx(String s)：该方法将指定字符串解析成无符号整数。当调用类为Integer时，xxx代表int；当调用类是Long时，xxx代表long。
     * ➢ static xxx parseUnsignedXxx(String s, int radix)：该方法将指定字符串按指定进制解析成无符号整数。当调用类为Integer时，xxx代表int；当调用类是Long时，xxx代表long。
     * ➢ static int compareUnsigned(xxx x, xxx y)：该方法将x、y两个整数转换为无符号整数后比较大小。当调用类为Integer时，xxx代表int；当调用类是Long时，xxx代表long。
     * ➢ static long divideUnsigned(long dividend, long divisor)：该方法将x、y两个整数转换为无符号整数后计算它们相除的商。当调用类为Integer时，xxx代表int；当调用类是Long时，xxx代表long。
     * ➢ static long remainderUnsigned(long dividend, long divisor)：该方法将x、y两个整数转换为无符号整数后计算它们相除的余数。当调用类为Integer时，xxx代表int；当调用类是Long时，xxx代表long。
     * <p>
     * Java 8还为Byte、Short增加了toUnsignedInt（xxx x）、toUnsignedLong（yyy x）两个方法，这两个方法用于将指定byte或short类型的变量或值转换成无符号的int或long值。
     */
    public static void unsignedTest() {
        byte b = -3;
        System.out.println("byte类型的-3对应的无符号整数：" + Byte.toUnsignedInt(b)); // 253
        // 指定使用十六进制解析无符号整数
        int val = Integer.parseUnsignedInt("ab", 16);
        System.out.println(val);    // 输出171
        // 将-12转换为无符号int型，然后转换为十六进制的字符串
        System.out.println(Integer.toUnsignedString(-12, 16));  // fffffff4
        // 将两个数转换为无符号整数后相除
        System.out.println(Integer.divideUnsigned(-2, 3));  // 1431655764
        // 将两个数转换为无符号整数相除后求余
        System.out.println(Integer.remainderUnsigned(-2, 7));   // 2
    }

    public static void main(String[] args) {
        autoBoxingUnboxing();
        primitive2String();
        wrapperClassCompare();
        unsignedTest();
    }
}
